

use crate::conf_parse::status::Status;
pub use expression::Expression;

pub mod ast;
pub mod expression;
pub mod status;
pub mod rules;

pub use ast::Ast;
use crate::conf_parse::expression::{FilterAst, FilterReturn, parse_nested, NestedInfo, ConnAst, ConnectInfo, parse_connect, ConnReturn};
use crate::conf_init::{MapResult, MapErr};

#[macro_export]
#[cfg_attr(feature = "cargo-clippy", allow(clippy::let_and_return))]
macro_rules! rules{
    ($($n:expr => $e:expr),*) =>{{
        use $crate::conf_parse::rules::{Rules};
        let rules = Rules::new();
        $(let rules = rules.add($n, $e);)*
        rules
    }};
}

#[macro_export]
macro_rules! lit {
    ($e:expr) => {{
        use $crate::conf_parse::expression::{Expression, Atom};
        Expression::Atom(Atom::Literal($e.to_string()))
    }};
}

#[macro_export]
macro_rules! dot{
    () => {{
        use $crate::conf_parse::expression::{Expression, Atom};
        Expression::Atom(Atom::Dot)
    }};
}

#[macro_export]
macro_rules! and{
    ($($e:expr),*) => {{
        use $crate::conf_parse::expression::{Expression, MultiExpr};
        Expression::And(MultiExpr(vec![$($e ,)*]))
    }};
}

#[macro_export]
macro_rules! or{
    ($($e:expr),*) => {{
        use $crate::conf_parse::expression::{Expression, MultiExpr};
        Expression::Or(MultiExpr(vec![$($e ,)*]))
    }};
    ($($e:ident),*) => {{
        use $crate::conf_parse::expression::{Expression, MultiExpr};
        Expression::Or(MultiExpr(vec![$($e ,)*]))
    }};
}

#[macro_export]
macro_rules! not{
    ($e:expr) => {{
        use $crate::conf_parse::expression::{Expression};
        Expression::Not($e)
    }};
}

#[macro_export]
macro_rules! rep{
    ($e:expr, $min:expr, $max:expr, $end:expr) => {{
        use $crate::conf_parse::expression::{Expression, RepInfo};
        Expression::Repeat(RepInfo::new(Box::new($e), Some($min), Some($max), Some(Box::new($end))))
    }};

    ($e:expr, , $max:expr, $end:expr) => {{
        use $crate::conf_parse::expression::{Expression, RepInfo};
        Expression::Repeat(RepInfo::new(Box::new($e), None, Some($max), Some(Box::new($end))))
    }};

    ($e:expr, $min:expr, , $end:expr) => {{
        use $crate::conf_parse::expression::{Expression, RepInfo};
        Expression::Repeat(RepInfo::new(Box::new($e), Some($min), None, Some(Box::new($end))))
    }};

    ($e:expr, $min:expr, $max:expr, ) => {{
        use $crate::conf_parse::expression::{Expression, RepInfo};
        Expression::Repeat(RepInfo::new(Box::new($e), Some($min), Some($max), None))
    }};

    ($e:expr, , , $end:expr) => {{
        use $crate::conf_parse::expression::{Expression, RepInfo};
        Expression::Repeat(RepInfo::new(Box::new($e), None, None, Some(Box::new($end))))
    }};

    ($e:expr, , $max:expr,) => {{
        use $crate::conf_parse::expression::{Expression, RepInfo};
        Expression::Repeat(RepInfo::new(Box::new($e), None, Some($max), None))
    }};

    ($e:expr, $min:expr, , ) => {{
        use $crate::conf_parse::expression::{Expression, RepInfo};
        Expression::Repeat(RepInfo::new(Box::new($e), Some($min), None, None))
    }};

    ($e:expr, , , ) => {{
        use $crate::conf_parse::expression::{Expression, RepInfo};
        Expression::Repeat(RepInfo::new(Box::new($e), None, None, None))
    }};
}

#[macro_export]
macro_rules! sub_rule{
    ($n:expr,$e:ident) => {{
        use $crate::conf_parse::expression::Expression;

        Expression::SubRule(($n.to_string(),Box::new($e.clone())))
    }};
}

pub fn parse(s: &str, root: &Expression, debug: bool)-> MapResult<Ast>{
    let status = Status::new(s);

    if debug{
        println!("parse, expr [{:?}] start chars [{:?}]", root, status.chars);
    }
    match expression::parse(status, root, debug){
        Ok((st, node)) => {
            if debug{
                println!("parse, expr [{:?}] ok chars [{:?}]", root, st.chars);
            }
            return MapResult::Ok(node);
        },
        Err(st) => {
            if debug{
                println!("parse, expr [{:?}] none chars [{:?}]", root, st.chars);
            }
            return MapResult::Err(MapErr::new(format!("parse [{}] expr [{:?}] failed", s, root)));
        },
    }
}
pub fn parse_filter(desc: &str, filter_expr: &Expression,
                    left_expr: &Expression, right_expr: &Expression,
                    and_expr: &Expression, or_expr: &Expression, debug: bool)
    ->MapResult<FilterAst>{

    let status = Status::new(desc);
    let nested_info = NestedInfo{
        expression: Box::new(filter_expr.clone()),
        left: Box::new(left_expr.clone()),
        right: Box::new(right_expr.clone()),
        and: Box::new(and_expr.clone()),
        or: Box::new(or_expr.clone()),
    };

    let mut location = 0;

    match parse_nested(status, nested_info, &mut location,debug){
        FilterReturn::Ok((_st, ast)) => {return MapResult::Ok(ast);},
        FilterReturn::Err((_st, err)) => {
            return MapResult::Err(MapErr::new(format!("filter parse err [{:?}]", err)));
        },
    }
}

pub fn parse_conn(desc:&str, base: &Expression, conn: &Expression,
                  space: &Expression, debug: bool)
    ->MapResult<ConnAst>{

    let conn_info = ConnectInfo{
        base: Box::new(base.clone()),
        conn: Box::new(conn.clone()),
        space: Box::new(space.clone()),
    };
    let status = Status::new(desc);
    let mut location = 0;

    match parse_connect(status, conn_info, &mut location, false){
        ConnReturn::Ok((_st, ast)) => {return MapResult::Ok(ast)},
        ConnReturn::Err((_st, err)) => {
            return MapResult::Err(MapErr::new(format!("conn parse err [{:?}]", err)));
        },
    }
}

#[cfg(test)]
mod test{
    use crate::conf_parse::parse;

    #[test]
    pub fn macro_test(){

        let func_arg_list = and!(lit!("["),rep!(dot!(),,,lit!("]")),lit!("]"));
        let func_arg_single = rep!(dot!(),,,or!(lit!(","),lit!(")")));
        let func_arg_enum = or!(sub_rule!("func_arg_list",func_arg_list),sub_rule!("func_arg_single",func_arg_single));
        let func_arg = and!(rep!(and!(sub_rule!("func_arg_enum",func_arg_enum),lit!(",")),,,),sub_rule!("func_arg_enum", func_arg_enum));
        let func_name = rep!(dot!(), 1, ,lit!("("));
        let root = and!(sub_rule!("func_name",func_name),lit!("("),sub_rule!("func_arg",func_arg),lit!(")"));

        //([a,b,c],dddd,5555,{(d,e,f)}_id)

        let desc = "myfunc([a,b,c],dddd)";

        let ast = parse(desc, &root, true);

        println!("root: {:?}", root);

        println!("ast: [{:?}]", ast);

    }
}


